/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.tabbedproperties.section;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.eclipse.core.runtime.Assert;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.commands.CommandStack;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.ClassifierTemplateParameter;
import org.eclipse.uml2.uml.TemplateParameter;
import org.eclipse.uml2.uml.TemplateableElement;

import com.cea.papyrus.core.commands.template.DeleteReferenceFromTemplateParameterCommand;
import com.cea.papyrus.core.editor.CommandStackUtils;
import com.cea.papyrus.core.editpart.IUMLElementEditPart;
import com.cea.papyrus.core.listener.UmlElementListener;
import com.cea.papyrus.core.tabbedproperties.PropertyViewSection;
import com.cea.papyrus.core.tabbedproperties.composite.DecoratedTextComposite;
import com.cea.papyrus.core.utils.LookForElement;
import com.cea.papyrus.diagram.classdiagram.commands.ElementReferencedByATemplateParameterCreateCommand;
import com.cea.papyrus.diagram.classdiagram.editpart.RedefinableTemplateSignatureEditPart;
import com.cea.papyrus.diagram.classdiagram.editpart.TemplateParameterEditPart;
import com.cea.papyrus.diagram.classdiagram.editpart.TemplateSignatureEditPart;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;
import com.cea.papyrus.diagraminterchange2.di2.Uml1SemanticModelBridge;
import com.cea.papyrus.profile.ui.dialogs.ChooseSetAssistedDialog;
import com.cea.papyrus.umlutils.Element;
import com.cea.papyrus.umlutils.Package;
import com.cea.papyrus.umlutils.Type;

/**
 * The template signature section on the element tab.
 * This section is added only when a TemplateableElement owns a TemplateSignature
 * (cf. TemplateSignatureSectionFilter)
 */
public class ConstrainingClassifierSection extends PropertyViewSection {

	/**
	 * 
	 */
	private ConstrainingClassifierComposite constrainingClassifierComposite;

	private com.cea.papyrus.umlutils.Package context ;
	
//	private TabbedPropertySheetPage sheetPage = null ;
	
	/* (non-Javadoc)
	 * @see org.eclipse.ui.views.properties.tabbed.AbstractPropertySection#createControls(org.eclipse.swt.widgets.Composite, org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage)
	 */
	/**
	 * 
	 * 
	 * @param tabbedPropertySheetPage 
	 * @param parent 
	 */
	@Override
	public void createControls(Composite parent,
			TabbedPropertySheetPage tabbedPropertySheetPage) {
		super.createControls(parent, tabbedPropertySheetPage);
		// creation of the constraining classifier section
		constrainingClassifierComposite = new ConstrainingClassifierComposite() ;
		constrainingClassifierComposite.createContent(parent, tabbedPropertySheetPage.getWidgetFactory());
	}

	/*
	 * @see org.eclipse.ui.views.properties.tabbed.view.ITabbedPropertySection#refresh()
	 */
	/**
	 * 
	 */
	@Override
	public void refresh() {
		constrainingClassifierComposite.refresh();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.ui.views.properties.tabbed.AbstractPropertySection#setInput(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection)
	 */
	/**
	 * 
	 * 
	 * @param part 
	 * @param selection 
	 */
	@Override
	public void setInput(IWorkbenchPart part, ISelection selection) {
		super.setInput(part, selection);
		Assert.isTrue(selection instanceof IStructuredSelection);
		Object input = ((IStructuredSelection) selection).getFirstElement();
		if (input instanceof IUMLElementEditPart) {
			org.eclipse.uml2.uml.Element element = ((IUMLElementEditPart) input).getUmlElement();
        	if(element instanceof org.eclipse.uml2.uml.TemplateParameter) {
        		com.cea.papyrus.umlutils.Element utilElement = new Element((org.eclipse.uml2.uml.TemplateParameter)element);
        		this.context = new com.cea.papyrus.umlutils.Package(utilElement.getUml2Element().getNearestPackage()) ; 
        		constrainingClassifierComposite.setElement(utilElement) ;
        		constrainingClassifierComposite.context = this.context ;
        	}
		}
	}
	
	/*
	 * (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.properties.tabbed.PropertyViewSection#dispose()
	 */
	/**
	 * 
	 */
	public void dispose(){
		super.dispose();
		if(constrainingClassifierComposite!=null)
			constrainingClassifierComposite.dispose();
	}
	
	private class ConstrainingClassifierComposite extends DecoratedTextComposite {
		public ConstrainingClassifierComposite() {
			super("Constraining classifier") ;
		}
		
		private Package context ;
		
		public void editText() {
			TemplateParameter currentTemplateParameter = (TemplateParameter)getElement().getUml2Element() ;
			if (currentTemplateParameter.getParameteredElement()!=null) {
				EditPart selection = null ;
				for (Iterator<EditPart> i=LookForElement.getAllOutlineEditParts().iterator() ; i.hasNext() ; ) {
					EditPart current = i.next() ;
					if (current.getModel() == currentTemplateParameter.getParameteredElement())
						selection = current ;
				}
				
//				if (selection != null)
//					sheetPage.selectionChanged(getPart(), new StructuredSelection(selection)) ;
			}
		}
		
		public void removeButtonPressed() {
			ClassifierTemplateParameter currentTemplateParameter = (ClassifierTemplateParameter)this.getElement().getUml2Element() ;
			if (currentTemplateParameter.getConstrainingClassifier()!=null) {
				CommandStack stack = CommandStackUtils.getCommandStack();
				org.eclipse.gef.commands.Command command = new DeleteReferenceFromTemplateParameterCommand(currentTemplateParameter, currentTemplateParameter.getConstrainingClassifier()) ;
				Assert.isNotNull(stack, "Impossible to adapt current editor into a CommandStack");
				stack.execute(command);
				refresh() ;
			}
		}
		
		public void addButtonPressed() {
			TemplateParameter currentTemplateParameter = (TemplateParameter)this.getElement().getUml2Element() ;
			
			ChooseFilteredClassifierDialog dialog = new ChooseFilteredClassifierDialog(composite.getShell(), 
														"Possible classifiers:", 
														"Selected classifier:",
														currentTemplateParameter.getParameteredElement().eClass(),
														context) ; 
			dialog.create();
			dialog.setFocus();
			dialog.open();
			if(dialog.getSelectedElement()!=null) {
				org.eclipse.gef.commands.Command command ;
				CommandStack stack = CommandStackUtils.getCommandStack();
				Class elementClass = null ;
				try {
					String className = "org.eclipse.uml2.uml." + dialog.getSelectedElement().eClass().getName() ;
					elementClass = Class.forName(className) ;
				}
				catch (Exception e) {
					e.printStackTrace() ;
				}
				command = new ElementReferencedByATemplateParameterCreateCommand(dialog.getSelectedElement(), elementClass, currentTemplateParameter, ElementReferencedByATemplateParameterCreateCommand.CONSTRAINING_CLASSIFIER) ;
				Assert.isNotNull(stack, "Impossible to adapt current editor into a CommandStack");
				stack.execute(command);
				refresh() ;
				if ((graphicalElement != null )&&(graphicalElement instanceof TemplateParameterEditPart)) {
					for (Iterator<EditPart> i = LookForElement.getAllEditParts(((EditPart)graphicalElement).getRoot()).iterator() ; i.hasNext() ; ) {
						EditPart current = i.next() ;
						if (current instanceof TemplateParameterEditPart) {
							if (((TemplateParameterEditPart)current).getUmlElement() == currentTemplateParameter) {
								TemplateParameterEditPart currentTemplateParameterEditPart = (TemplateParameterEditPart)current ;
								UmlElementListener _adapter=new UmlElementListener((TemplateParameterEditPart)current) ;
								((Notifier)((ClassifierTemplateParameter)currentTemplateParameter).getConstrainingClassifier()).eAdapters().add(_adapter) ;
								current.refresh() ;
							}
						}
					}
				}
			}
		}
		
		public void refresh() {
			if (text.isDisposed()) return ;
			TemplateParameter currentTemplateParameter = (TemplateParameter)this.getElement().getUml2Element() ;
			String elementName = "" ;
			boolean controlEnabled = true ;
			if (graphicalElement != null && graphicalElement instanceof TemplateParameterEditPart) {
				// this test determines if currentTemplateParameter is displayed in a context where it is owned, or in a context where it is referenced
				// if it is referenced, related constrol (add, remove, etc.) must be disabled
				TemplateParameterEditPart editPart = (TemplateParameterEditPart)graphicalElement;
				Uml1SemanticModelBridge semanticModel ;
				if (editPart.getParent().getParent() instanceof RedefinableTemplateSignatureEditPart) {
					RedefinableTemplateSignatureEditPart toto = (RedefinableTemplateSignatureEditPart)editPart.getParent().getParent() ;
					semanticModel = (Uml1SemanticModelBridge)((GraphElement)toto.getGraphNode().getSemanticParent()).getSemanticModel() ;
				}
				else {
					TemplateSignatureEditPart toto = (TemplateSignatureEditPart)editPart.getParent().getParent() ;
					semanticModel = (Uml1SemanticModelBridge)((GraphElement)toto.getGraphNode().getSemanticParent()).getSemanticModel() ;
				}
				if (!((TemplateableElement)semanticModel.getElement()).getOwnedTemplateSignature().getOwnedParameters().contains(currentTemplateParameter)) {
					controlEnabled = false ;
				}
			}
			if (currentTemplateParameter.getParameteredElement() != null) {
				this.label.setEnabled(controlEnabled) ;
				this.text.setEnabled(controlEnabled) ;
				this.addButton.setEnabled(controlEnabled) ;
				this.removeButton.setEnabled(controlEnabled) ;
				if (((ClassifierTemplateParameter)currentTemplateParameter).getConstrainingClassifier() != null)
					elementName = ((ClassifierTemplateParameter)currentTemplateParameter).getConstrainingClassifier().getName() ; 
			}
			else {
				this.label.setEnabled(false) ;
				this.text.setEnabled(false) ;
				this.addButton.setEnabled(false) ;
				this.removeButton.setEnabled(false) ;
			}
			if (elementName != null)
				this.text.setText(elementName) ;
			else
				this.text.setText("") ;
		}
	}
	
	// ChooseSetAssistedDialog is specialized locally
	protected class ChooseFilteredClassifierDialog extends ChooseSetAssistedDialog {
		
		private String selectedClassifierName ;
		
		private Map<String, org.eclipse.uml2.uml.Element> accessibleElementsMap ;
		
		private Package context ;
		
		public ChooseFilteredClassifierDialog(Shell parentShell, String possibleText, String selectedText, EClass filter, Package context){
			super(parentShell, possibleText, selectedText) ;
			this.context = context ;
			
			List<Type> allAccessibleTypes = new ArrayList<Type>(context.getAccessibleTypes()) ;
			allAccessibleTypes.addAll(context.getAccesibleAssociation()) ;
			List<org.eclipse.uml2.uml.Classifier> compatibleMetaclasses = LookForElement.getMetaclasses(filter, false, context.getUml2Element()) ;
			
			accessibleElementsMap = new HashMap<String, org.eclipse.uml2.uml.Element>() ;
			
			for (Iterator<Type> i = allAccessibleTypes.iterator(); i.hasNext() ; ) {
				com.cea.papyrus.umlutils.Type current = i.next() ;
				for (Iterator<org.eclipse.uml2.uml.Classifier> j = compatibleMetaclasses.iterator() ; j.hasNext() ;	) {
					Classifier currentMetaClass = j.next() ;
					if (current.getUml2Element().eClass().getName().equals(currentMetaClass.getName())) {
						if(!selectedElementList.contains(current)) { 
							possibleElementList.addElement(current.getQualifiedName());
							accessibleElementsMap.put(current.getQualifiedName(), current.getUml2Element()) ;
						}
					}
				}
			}
		}
		
		// create method is overrided so that up and down buttons are not visible
		// Only one metaclass can be selected, so these features are useless
		public void create() {
			super.create() ;
			downButton.setVisible(false) ;
			upButton.setVisible(false) ;
		}
			
		public void runActionAdd(Object element) {
			super.runActionAdd(element) ;
			selectedClassifierName = new String((String)element) ;
			this.addButton.setEnabled(false) ;
		}
		
		protected void runActionRemove(Object element){
			super.runActionRemove(element) ;
			this.addButton.setEnabled(true) ;
			this.selectedClassifierName = null ;
		}
	
		public void cancelPressed() {
			selectedClassifierName = null ;
			super.cancelPressed() ;
		}
		
		public void okPressed() {
			if (selectedClassifierName!=null)
				super.okPressed() ;
		}
		
		public org.eclipse.uml2.uml.Element getSelectedElement() {
			if (selectedClassifierName == null)
				return null ;
			else {
				return accessibleElementsMap.get(selectedClassifierName) ;
			}
		}
		
	}
	
}

